TTS log visibility + ElevenLabs mispronunciations
# support
n
Call ID:
019e50a7-8028-7000-a200-3dee116f0fb7
Two questions for an outbound call using 11labs voice, eleven_flash_v2_5, with chunkPlan.formatPlan.enabled: true (plus regex replacements that turn 8 9 0 0 into 8, 9, 0, 0). 1. Is there any way to see the exact text Vapi sends to ElevenLabs after formatPlan runs? The call-log export contains completionText (LLM output) and the Deepgram readback, but no event carries the formatted TTS input to ElevenLabs — Bot started speaking and pipeline.sayQueuePush both have no text field. Without seeing the post-format string, I can't tell whether my replacements are working as expected. 2. How do you recommend fixing these two mispronunciations? * The first of the two trailing zeros in a phone number like +1 617-423-8900 is audibly under-articulated (does not sound like a zero), even though Deepgram transcribes it correctly as 8 9 0 0. Comma-separation via formatPlan.replacements doesn't fix it (supposedly, but cant check if it applies due to issue #1. * Address abbreviations like St are pronounced as "Saint" instead of "Street". The same source text pasted into the ElevenLabs web playground (same model) pronounces both correctly. How can I make it behave just like on the ElevenLabs web playground?
s
Hi, We dug into your call (019e50a7-8028-7000-a200-3dee116f0fb7) and have answers for both.
1. Viewing the post-formatPlan text sent to ElevenLabs Unfortunately, this isn't possible right now. Vapi's internal logging only captures metadata for TTS events (provider, model, latency, character count) - not the actual formatted text string. The completionText field in your call logs is the raw LLM output before chunking and formatting. There's no log field, API endpoint, or export that shows what was sent to ElevenLabs after formatPlan runs.
For now, the workaround is to reason about it manually: take the raw completionText, apply your replacement rules yourself, and that should match what Vapi sent.
2a. Trailing-zero mispronunciation in phone numbers Your current regex (\b(\d) (\d)\b → $1, $2) only matches single digits separated by a space. The LLM is outputting the phone number as +1 617-423-8900 - the 8900 is a contiguous string with no spaces, so your regex never fires on it. To fix this, add a regex that breaks apart contiguous digits:
Copy code
{
  "type": "regex",
  "regex": "(\\d)(\\d)(\\d)(\\d)(?!\\d)",
  "value": "$1, $2, $3, $4"
}
This turns 8900 into 8, 9, 0, 0 before it reaches ElevenLabs. The commas force the model to articulate each digit separately.
Even more reliable: instruct your LLM in the system prompt to always read phone numbers digit by digit with commas (e.g., "6, 1, 7, 4, 2, 3, 8, 9, 0, 0"). This removes the dependency on formatPlan entirely.
2b. "St" pronounced as "Saint" instead of "Street" This is a chunking issue. Vapi's chunkPlan splits text on punctuation boundaries before sending each piece to ElevenLabs. In your call, the chunk ends with St, - and with nothing after it in that chunk, ElevenLabs lacks the context to infer "Street" and defaults to "Saint." In the ElevenLabs playground, the full paragraph is sent at once, so the model sees Apt 1500 right after St, and gets it right. The fix is to pre-expand the abbreviation in formatPlan.replacements before chunking happens: [ { "type": "exact", "key": " St,", "value": " Street," }, { "type": "exact", "key": " St.", "value": " Street." }, { "type": "exact", "key": " St ", "value": " Street " } ] The leading space in each key prevents accidentally replacing "St" inside words like "Stock" or "Start." The general principle: the ElevenLabs playground sounds better because it receives the full text with full context. Vapi sends one chunk at a time, so ambiguous abbreviations lose their surrounding context. The fix is always to expand ambiguous text in formatPlan.replacements before it hits the chunk boundary.
2b. "St" pronounced as "Saint" instead of "Street" This is a chunking issue. Vapi's chunkPlan splits text on punctuation boundaries before sending each piece to ElevenLabs. In your call, the chunk ends with St, - and with nothing after it in that chunk, ElevenLabs lacks the context to infer "Street" and defaults to "Saint." In the ElevenLabs playground, the full paragraph is sent at once, so the model sees Apt 1500 right after St, and gets it right.
The fix is to pre-expand the abbreviation in formatPlan.replacements before chunking happens:
Copy code
[
  { "type": "exact", "key": " St,", "value": " Street," },
  { "type": "exact", "key": " St.", "value": " Street." },
  { "type": "exact", "key": " St ", "value": " Street " }
]
The leading space in each key prevents accidentally replacing "St" inside words like "Stock" or "Start."
The general principle: the ElevenLabs playground sounds better because it receives the full text with full context. Vapi sends one chunk at a time, so ambiguous abbreviations lose their surrounding context. The fix is always to expand ambiguous text in formatPlan.replacements before it hits the chunk boundary.
n
Thank you for such a detailed analysis, this really helped me. Regarding 2a. the reason why I had "(\b(\d) (\d)\b → $1, $2) only matches single digits separated by a space" because I thought the Vapi's default behaviour was rewriting all phone numbers to single digits via
voice.chunkPlan.formatPlan
with
phoneNumber
which is supposedly enabled by default. So the way I understand; 1. LLM generates raw text. 2.
voice.chunkPlan.formatPlan
formats phone numbers to separate single digits. 3. My regex replacements are applied that adds "," between single spaced digits. Can you please tell me if that is the case or not? FYI it would be extremely useful if we could view post-format plan without having to just guess.
s
Hi, Great question - your understanding of the pipeline is correct:
LLM generates raw text (e.g., "call +1 617-423-8900") Built-in formatters run, including the phoneNumber formatter (enabled by default) — this strips the +1 prefix, removes all formatting, and outputs space-separated digits: "6 1 7 4 2 3 8 9 0 0"
Your custom formatPlan.replacements run after the built-in formatters So your regex is operating on the right input. The issue is that \b(\d) (\d)\b only matches one pair per pass, so it won't catch all adjacent digits in a single run.
A more reliable approach is to use voice.phoneNumberDigitPauseSeconds - for example, setting it to 0.5 changes the formatter output to use SSML breaks between digit groups:
"Six One Seven Four Two Three Eight Nine Zero Zero" ElevenLabs handles this much more cleanly than comma separation, and you wouldn't need your custom regex at all.
n
Thank you Shaunak, this is exactly what I was looking for! But in the [docs](https://docs.vapi.ai/api-reference/assistants/create#request.body.voice.ElevenLabsVoice) I dont see it being an option (note I am using ElevenLabs). I can see there is
enableSsmlParsing
option but I dont want to manually (or let AI use its context) to insert these break tags. Are the docs out of date or is it not available for ElevenLabs voice?
s
Hi,
phoneNumberDigitPauseSeconds
is deprecated for ElevenLabs and hidden from the docs, which is why you're not seeing it. It was removed because it can cause hallucinations with ElevenLabs models.
For ElevenLabs specifically, the recommended approach is to enable
enableSsmlParsing
and use
formatPlan.replacements
to insert SSML breaks:
Copy code
json
{
  "provider": "11labs",
  "voiceId": "your-voice-id",
  "enableSsmlParsing": true,
  "chunkPlan": {
    "formatPlan": {
      "replacements": [
        {
          "type": "regex",
          "regex": "(\\d) (\\d) (\\d) (\\d)",
          "value": "$1 $2 $3 $4 <break time=\"0.5s\" />"
        }
      ]
    }
  }
}
Since the built-in phone number formatter already outputs space-separated digits (
6 1 7 4 2 3 8 9 0 0
), you just need the replacement to insert
<break>
tags where you want pauses between digit groups. Adjust the regex pattern to match your preferred grouping. The key is
enableSsmlParsing: true
without it, ElevenLabs will speak the tags literally instead of processing them.
The key is
enableSsmlParsing: true
without it, ElevenLabs will speak the tags literally instead of processing them.
The key is
enableSsmlParsing: true
without it, ElevenLabs will speak the tags literally instead of processing them.
n
Thank you, yeah with break tags I can see it hallucinate (call id
019e72bb-3c4f-7444-93b3-c9b4f68c1584
), so I will revert to what I had originally. Thanks for trying though.
s
No worries - sorry that approach didn't work out.
If you run into anything else, feel free to open a new ticket and we'll be happy to help.